﻿//==============================================================================
// File: TreeViewBahaviors.cs
// Created: 2010-05-28
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with Mammoth. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfRcp.Controls.Behaviors
{
    public static class TreeViewBahaviors
    {
        #region CommandTreeViewBehavior Attached Property

        private static readonly DependencyProperty CommandTreeViewBehaviorProperty =
            DependencyProperty.RegisterAttached("CommandTreeViewBehavior", typeof(CommandTreeViewBehavior),
                                                typeof(TreeViewBahaviors), null);

        private static CommandTreeViewBehavior GetCommandTreeViewBehavior(DependencyObject obj)
        {
            return (CommandTreeViewBehavior)obj.GetValue(CommandTreeViewBehaviorProperty);
        }

        private static void SetCommandTreeViewBehavior(DependencyObject obj, CommandTreeViewBehavior value)
        {
            obj.SetValue(CommandTreeViewBehaviorProperty, value);
        }

        #endregion

        #region Command Attached Property

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(TreeViewBahaviors),
                                                new PropertyMetadata(CommandPropertyChanged));

        public static ICommand GetCommand(TreeView treeView)
        {
            return (ICommand)treeView.GetValue(CommandProperty);
        }

        public static void SetCommand(TreeView treeView, ICommand value)
        {
            treeView.SetValue(CommandProperty, value);
        }

        private static void CommandPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var element = o as TreeView;
            if (element != null)
            {
                if (e.OldValue != null)
                {
                    UnhookCommand(element);
                }
                if (e.NewValue != null)
                {
                    HookCommand(element, (ICommand)e.NewValue);
                }
            }
        }

        #endregion

        #region CommandParameter Attached Property

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(TreeViewBahaviors),
                                                null);

        public static object GetCommandParameter(TreeView treeView)
        {
            return treeView.GetValue(CommandParameterProperty);
        }

        public static void SetCommandParameter(TreeView treeView, object value)
        {
            treeView.SetValue(CommandParameterProperty, value);
        }

        #endregion

        #region Command Hooking / Unhooking

        private static void HookCommand(TreeView element, ICommand command)
        {
            var behavior = new CommandTreeViewBehavior(element, command);
            behavior.Attach();
            SetCommandTreeViewBehavior(element, behavior);
        }

        private static void UnhookCommand(DependencyObject element)
        {
            var behavior = GetCommandTreeViewBehavior(element);
            behavior.Detach();

            element.ClearValue(CommandTreeViewBehaviorProperty);
        }

        #endregion

        private class CommandTreeViewBehavior
        {
            private readonly WeakReference m_ElementReference;
            private readonly ICommand m_Command;

            public CommandTreeViewBehavior(TreeView element, ICommand command)
            {
                m_ElementReference = new WeakReference(element);
                m_Command = command;
            }

            public void Attach()
            {
                var element = GetElement();
                if (element != null)
                {
                    element.SelectedItemChanged += OnSelectedItemChangedHandler;
                    m_Command.CanExecuteChanged += CanExecuteChangedHandler;
                    SetIsEnabled(element);
                }
            }

            private static void OnSelectedItemChangedHandler(object sender, RoutedPropertyChangedEventArgs<object> e)
            {
                var element = (TreeView)sender;
                var command = GetCommand(element);
                var commandParameter = GetCommandParameter(element);
                command.Execute(new SelectedItemChangedCommandParameter(commandParameter, e));
            }

            public void Detach()
            {
                m_Command.CanExecuteChanged -= CanExecuteChangedHandler;
                var element = GetElement();
                if (element != null)
                    element.SelectedItemChanged -= OnSelectedItemChangedHandler;
            }

            private void CanExecuteChangedHandler(object sender, EventArgs e)
            {
                var element = GetElement();
                if (element != null)
                    SetIsEnabled(element);
                else
                    Detach();
            }

            private void SetIsEnabled(TreeView element)
            {
                var commandParameter = GetCommandParameter(element);
                element.IsEnabled = m_Command.CanExecute(commandParameter);
            }

            private TreeView GetElement()
            {
                return m_ElementReference.Target as TreeView;
            }
        }

        public class SelectedItemChangedCommandParameter
        {
            public SelectedItemChangedCommandParameter(
                object commandParameter,
                RoutedPropertyChangedEventArgs<object> eventArgs)
            {
                EventArgs = eventArgs;
                CommandParameter = commandParameter;
            }

            public object CommandParameter { get; private set; }

            public RoutedPropertyChangedEventArgs<object> EventArgs { get; private set; }
        }
    }
}